﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Pikapika.Models;
using System.ServiceModel.Syndication;
using System.Web.Routing;
using Pikapika.Models.ViewModels.Home;
using System.Data.Objects;

namespace Pikapika.Controllers
{
    public class FeedController : Controller
    {
        private const long TicksPerHour = 0x861c46800L;

        private Uri GenerateNovelChapterUri(NovelChapter ch)
        {
            return new Uri(Url.Action("Read", "Novel", new RouteValueDictionary { { "id", ch.Id }, { "novelName", ch.NovelVolume.Novel.Name }, { "volumeName", ch.NovelVolume.Name }, { "chapterName", ch.Name } }, Request.Url.Scheme, Request.Url.Host));
        }
        public ActionResult LatestChapterSummary(int id, int collapseHours = 3, int maxItems = 10)
        {
            if (collapseHours < 1)
            {
                return new HttpStatusCodeResult(400, "Bad Request");
            }
            if (maxItems < 1)
            {
                return new HttpStatusCodeResult(400, "Bad Request");
            }
            using (var ctx = new PikapikaDBEntities())
            {
                var result = (ObjectQuery<NovelChapter>)(from ch in ctx.NovelChapters
                                                         where ch.NovelVolume.NovelId == id
                                                         orderby ch.SortIndex descending, ch.UpdatedDate descending, ch.Id descending
                                                         select ch);
                result = result.Include("NovelVolume.Novel");
                var groups = new List<List<NovelChapter>>();
                DateTime? currentTime = null;
                int? currentNovelVolumeId = null;
                var currentGroup = new List<NovelChapter>();
                foreach (var ch in result)
                {
                    if (!currentTime.HasValue || (currentTime.Value - ch.UpdatedDate).TotalHours > collapseHours || ch.NovelVolumeId != currentNovelVolumeId.Value)
                    {
                        if (groups.Count >= maxItems)
                        {
                            break;
                        }
                        currentTime = ch.UpdatedDate;
                        currentNovelVolumeId = ch.NovelVolumeId;
                        currentGroup = new List<NovelChapter>();
                        groups.Add(currentGroup);
                    }
                    currentGroup.Add(ch);
                }
                if (groups.Count == 0)
                {
                    return HttpNotFound();
                }
                IEnumerable<SyndicationItem> feedItems =
                    groups.Select(o =>
                    {
                        NovelChapter latestCh = o.First();
                        var ret = new SyndicationItem(
                         string.Format("{0} - {1} ({2} ~ {3})", latestCh.NovelVolume.Novel.Name, latestCh.NovelVolume.Name, o.Last().Name, latestCh.Name),
                         "",
                         GenerateNovelChapterUri(o.Last()),
                         string.Format("urn:pikapika:novel:item:{0}:{1}:range:{2}:{3}", latestCh.NovelVolume.Novel.Id, latestCh.NovelVolume.Id, o.Last().Id, latestCh.Id),
                         new DateTimeOffset(latestCh.UpdatedDate, TimeSpan.Zero));
                        ret.Authors.Add(new SyndicationPerson { Name = latestCh.NovelVolume.Novel.Author });
                        return ret;
                    });
                SyndicationFeed feed = new SyndicationFeed(feedItems);
                feed.Id = string.Format("urn:pikapika:novel:{0}:feed:update-summary", id);
                feed.Title = new TextSyndicationContent(string.Format("PikaPika - {0} - 更新摘要", groups.First().First().NovelVolume.Novel.Name));
                feed.LastUpdatedTime = new DateTimeOffset(groups.First().First().UpdatedDate, TimeSpan.Zero);
                return new FeedResult(feed);
            }
        }

        public ActionResult LatestChapters(int id, int maxItems = 10)
        {
            if (maxItems < 1)
            {
                return new HttpStatusCodeResult(400, "Bad Request");
            }
            using (var ctx = new PikapikaDBEntities())
            {
                var query = (ObjectQuery<NovelChapter>)(from ch in ctx.NovelChapters
                                                         where ch.NovelVolume.NovelId == id
                                                         orderby ch.SortIndex descending, ch.UpdatedDate descending, ch.Id descending
                                                         select ch).Take(maxItems);
                query = query.Include("NovelVolume.Novel");
                var result = query.ToList();
                IEnumerable<SyndicationItem> feedItems =
                    result.Select(o =>
                    {
                        var ret = new SyndicationItem(
                         string.Format("{0} - {1} - {2}", o.NovelVolume.Novel.Name, o.NovelVolume.Name, o.Name),
                         SyndicationContent.CreateHtmlContent(o.ChapterContent),
                         GenerateNovelChapterUri(o),
                         string.Format("urn:pikapika:novel:item:{0}:{1}:{2}", o.NovelVolume.Novel.Id, o.NovelVolume.Id, o.Id),
                         new DateTimeOffset(o.UpdatedDate, TimeSpan.Zero));
                        ret.Authors.Add(new SyndicationPerson { Name = o.NovelVolume.Novel.Author });
                        return ret;
                    }).ToList();
                if (feedItems.Count() == 0)
                {
                    return HttpNotFound();
                }
                SyndicationFeed feed = new SyndicationFeed(feedItems);
                feed.Id = string.Format("urn:pikapika:novel:{0}:feed:update-summary", id);
                feed.Title = new TextSyndicationContent(string.Format("PikaPika - {0}", result.First().NovelVolume.Novel.Name));
                feed.LastUpdatedTime = feedItems.First().LastUpdatedTime;
                return new FeedResult(feed);
            }
        }
        public ActionResult LatestNovels(int? cooldownHours, int? maxItems)
        {
            using (var ctx = new PikapikaDBEntities())
            {
                IEnumerable<IndexLatestNovel> result = ctx.GetLatestNovels(maxItems ?? Properties.Settings.Default.HomeLatestNovelDisplayCount);
                if (cooldownHours.HasValue)
                {
                    result = result.Where(n => n.Novel.UpdatedDate < DateTime.UtcNow.AddHours(-cooldownHours.Value));
                }
                IEnumerable<SyndicationItem> feedItems =
                    result.Select(o =>
                    {
                        var ret = new SyndicationItem(
                         string.Format("{0} - {1}", o.Novel.Name, o.LatestVolume.Name),
                         o.LatestVolume.Description ?? "",
                         new Uri(Url.Action("Details", "Novel", new RouteValueDictionary { { "id", o.Novel.Id }, { "source", "feed!" + o.LatestVolume.Id.ToString() }, { "novelName", o.Novel.Name } }, Request.Url.Scheme, Request.Url.Host)),
                         string.Format("urn:pikapika:novel:item:{0}:{1}#{2}", o.Novel.Id, o.LatestVolume.Id, o.LatestChapter.Id),
                         new DateTimeOffset(o.Novel.UpdatedDate, TimeSpan.Zero));
                        ret.Authors.Add(new SyndicationPerson { Name = o.Novel.Author });
                        return ret;
                    });
                SyndicationFeed feed = new SyndicationFeed(feedItems);
                feed.Id = "urn:pikapika:novel:feed";
                feed.Title = new TextSyndicationContent("PikaPika - 最近更新");
                feed.LastUpdatedTime = (from n in result select new DateTimeOffset(n.Novel.UpdatedDate, TimeSpan.Zero)).FirstOrDefault();
                feed.Links.Add(SyndicationLink.CreateSelfLink(new Uri(Url.Action((string)this.RouteData.Values["action"], (string)this.RouteData.Values["controller"], null, Request.Url.Scheme, Request.Url.Host))));
                feed.Links.Add(SyndicationLink.CreateAlternateLink(new Uri(Url.Action("Index", "Home", null, Request.Url.Scheme, Request.Url.Host))));
                return new FeedResult(feed);
            }
        }
    }
}
